package org.jdsm.flow.core.utils.impl.jpa;

import org.jdsm.flow.core.base.dao.IGenericDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Generic DAO. General dao for JPA
 * User: mikom
 */
public abstract class GenericDao<T, K> implements IGenericDao<T, K> {

    private static final Logger log = LoggerFactory.getLogger(GenericDao.class);

    @PersistenceContext
    protected EntityManager em;

    @PersistenceUnit
    protected EntityManagerFactory entityManagerFactory;

    private Class<T> type;

    public GenericDao() {
        Type t = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) t;
        type = (Class<T>) pt.getActualTypeArguments()[0];
    }

    public EntityManager getEntityManager() {
        if (em == null || !em.isOpen()) {
            em = entityManagerFactory.createEntityManager();
        }
        return em;
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            em.close();
        } catch (Exception e) {
        }
        super.finalize();
    }

    @Override
    public T save(final T t) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            entityManager.getTransaction().begin();
            entityManager.persist(t);
            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
        return t;
    }

    @Override
    public void delete(final K id) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            entityManager.getTransaction().begin();
            T instance = entityManager.find(type, id);
            if (instance == null) {
                log.error("can't find entity by id : {}", id);
                return;
            }
            entityManager.remove(instance);
            entityManager.getTransaction().commit();
            log.debug("entity: {} was removed", instance);
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
        } finally {
            entityManager.close();
        }
    }

    @Override
    public T find(final K id) {
        if(id == null){
            log.error("entity id can't be null");
            return null;
        }
        return (T) getEntityManager().find(type, id);
    }

    @Override
    public T update(final T t) {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        T instance = null;
        try {
            entityManager.getTransaction().begin();
            instance = entityManager.merge(t);
            entityManager.getTransaction().commit();
            log.debug("entity was updated: {}", t);
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
            log.error("can't update entity: {}", t);
        } finally {
            entityManager.close();
        }
        return instance;
    }

    public EntityManagerFactory getEntityManagerFactory() {
        return entityManagerFactory;
    }

    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }
}
